package com.atguigu.gmall.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.order.client.OrderFeignClient;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.payment.config.AlipayConfig;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.atguigu.gmall.rabbit.config.MqConst;
import com.atguigu.gmall.rabbit.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-09-19 10:56
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class AlipayServiceImpl implements AlipayService {

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 基于订单生成支付页面
     *
     * @param userId
     * @param orderId
     * @return 支付宝响应支付页面表单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createAlipayForm(String userId, Long orderId) {
        try {
            //1.判断订单是否属于当前用户
            //1.1 远程调用订单服务得到订单信息
            OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId);
            if (orderInfo == null) {
                throw new RuntimeException("订单不存在！");
            }
            //1.2 判断订单是否属于当前用户；判断订单状态是否为未支付
            if (!userId.equals(orderInfo.getUserId().toString())) {
                throw new RuntimeException("参数有误！");
            }
            if (!PaymentStatus.UNPAID.name().equals(orderInfo.getOrderStatus())) {
                throw new RuntimeException("订单状态不正确！");
            }

            //2.保存本地交易记录
            //2.1 构建本地交易对象
            PaymentInfo paymentInfo = new PaymentInfo();
            paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
            paymentInfo.setOrderId(orderId);
            paymentInfo.setUserId(Long.valueOf(userId));
            paymentInfo.setPaymentType(PaymentType.ALIPAY.name());
            paymentInfo.setSubject(orderInfo.getTradeBody());
            //paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
            //测试阶段设置为1分
            paymentInfo.setTotalAmount(new BigDecimal("0.01"));
            paymentInfo.setPaymentStatus(PaymentStatus.UNPAID.name());
            //2.2 执行保存
            paymentInfoService.savePaymentInfo(paymentInfo, PaymentType.ALIPAY.name());

            //3.调用支付宝接口生成支付表单
            //3.1 构建支付宝页面请求对象
            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();


            //AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            //异步接收地址，仅支持http/https，公网可访问-支付宝调用支付系统通知支付结果-给系统
            request.setNotifyUrl(AlipayConfig.notify_payment_url);
            //同步跳转地址，仅支持http/https 支付宝通过浏览器同步回调展示支付成功页面-给用户
            request.setReturnUrl(AlipayConfig.return_payment_url);
            //3.2 构建相关请求参数
            /******必传参数******/
            JSONObject bizContent = new JSONObject();
            //商户订单号，商家自定义，保持唯一性
            bizContent.put("out_trade_no", orderInfo.getOutTradeNo());
            //支付金额，最小值0.01元
            bizContent.put("total_amount", paymentInfo.getTotalAmount());
            //订单标题，不可使用特殊符号
            bizContent.put("subject", orderInfo.getTradeBody());
            //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
            //bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
            bizContent.put("product_code", "QUICK_WAP_WAY");

            /******可选参数******/
            //支付宝支付二维码有效时间
            bizContent.put("timeout_express", "5m");
            request.setBizContent(bizContent.toString());
            //3.3 执行请求获取响应支付页面表单
            AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                //返回支付页面表单
                return response.getBody();
            }
        } catch (Exception e) {
            throw new RuntimeException("支付页面生成有误！");
        }
        return null;
    }

    /**
     * 当用户支付成功后，支付宝会自动调用该接口进行支付结果通知
     *
     * @param pramsMap
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String notifyPaySuccess(Map<String, String> paramsMap) {
        log.info(paramsMap.toString());
        //1.进行幂等性处理 set nx
        String notifyId = (String) paramsMap.get("notify_id");
        String key = "alipay:notify:" + notifyId;
        try {
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, notifyId, 25, TimeUnit.HOURS);
            if (!flag) {
                return "success";
            }
            //2.对提交参数进行验签，防止恶意“虚假通知”以及数据在网络传输中被篡改  //调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
            if (!signVerified) {
                log.error("[支付服务]支付宝验签失败：{}", paramsMap.toString());
                return "failure";
            }

            //3.验证订单是否属于商家
            String outTradeNo = paramsMap.get("out_trade_no");
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
            if (paymentInfo == null) {
                return "failure";
            }
            if (!outTradeNo.equals(paymentInfo.getOutTradeNo())) {
                return "failure";
            }
            //4.验证用户支付金额跟商家端金额是否一致
            String totalAmount = paramsMap.get("total_amount");
            if (new BigDecimal(totalAmount).compareTo(paymentInfo.getTotalAmount()) != 0) {
                return "failure";
            }

            //5.验证商户端APPID跟支付宝提交是否一致
            String appId = paramsMap.get("app_id");
            if (!AlipayConfig.app_id.equals(appId)) {
                return "failure";
            }

            //6.验证用户支付状态
            String tradeStatus = paramsMap.get("trade_status");
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                //7.修改本地交易记录 将交易状态改为：“已支付”
                paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());
                //支付宝交易号
                String tradeNo = paramsMap.get("trade_no");
                paymentInfo.setTradeNo(tradeNo);
                paymentInfo.setCallbackTime(new Date());
                paymentInfo.setCallbackContent(paramsMap.toString());
                paymentInfoService.updateById(paymentInfo);

                //8.基于MQ通知订单系统，修改订单状态
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderId());
                return "success";
            }
            return null;
        } catch (Exception e) {
            redisTemplate.delete(key);
            throw new RuntimeException(e);
        }
    }

    /**
     * 支付宝退款
     *
     * @param orderId
     * @return
     */
    @Override
    public Result refund(Long orderId) {
        try {
            //1.根据订单ID查询本地交易记录
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(orderId, PaymentType.ALIPAY.name());
            if (paymentInfo == null) {
                throw new RuntimeException("交易不存在");
            }

            //2.判断支付状态；修改本地交易记录状态；调用支付宝进行退款
            if (!PaymentStatus.UNPAID.name().equals(paymentInfo.getPaymentStatus())) {
                //调用支付宝进行退款
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
                JSONObject bizContent = new JSONObject();
                String tradeNo = paymentInfo.getTradeNo();
                if (StringUtils.isNotBlank(tradeNo)) {
                    //支付宝交易号
                    bizContent.put("trade_no", tradeNo);
                }
                String outTradeNo = paymentInfo.getOutTradeNo();
                if (StringUtils.isNotBlank(outTradeNo)) {
                    bizContent.put("out_trade_no", outTradeNo);
                }
                bizContent.put("refund_amount", paymentInfo.getTotalAmount());
                bizContent.put("out_request_no", "HZ01RF001");

                request.setBizContent(bizContent.toString());
                AlipayTradeRefundResponse response = alipayClient.execute(request);
                if (response.isSuccess() && "Y".equals(response.getFundChange())) {
                    //更新本地交易记录状态
                    paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
                    paymentInfoService.updateById(paymentInfo);
                    //3.TODO 发送MQ通知，通知订单系统关闭订单 单独提供MQ交换机+队列+绑定
                    return Result.ok();
                }
            }
            return Result.fail();
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将可能产生本地交易记录跟支付宝交易记录同样关闭
     *
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processCloseTrade(Long orderId) {
        //1.先根据订单ID+支付方式查询本地交易记录 如果有该记录将状态改为关闭
        PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(orderId, PaymentType.ALIPAY.name());
        if (paymentInfo != null) {
            paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
            paymentInfoService.updateById(paymentInfo);
            //2.调用支付宝接口查询是否产生支付宝交易，如果存在且未支付，将支付宝交易关闭（二维码也就不能付款）
            //2.1 根据订单编号查询支付宝端交易状态
            String tradeStatus = this.getAlipayTradeStatus(paymentInfo.getOutTradeNo());
            if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
                //2.2 如果支付宝端交易状态为待支付，执行关闭交易
                this.closeAlipayTrade(paymentInfo.getOutTradeNo());
            }
        }
    }

    /**
     * 查询支付宝交易是否存在，如果存在判断状态
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public String getAlipayTradeStatus(String outTradeNo) {
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                return response.getTradeStatus();
            } else {
                System.out.println("调用失败");
            }
            return null;
        } catch (Exception e) {
            //用户未扫码就会查询异常
            return null;
        }
    }

    /**
     * 关闭支付宝交易
     *
     * @param outTradeNo
     */
    @Override
    public void closeAlipayTrade(String outTradeNo) {
        try {
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeCloseResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("支付宝关闭交易调用成功");
            } else {
                log.info("支付宝关闭交易调用失败");
            }
        } catch (Exception e) {
        }
    }
}
